package ee.az.tetris;

import ee.az.tetris.event.BlocksFixedEvent;
import ee.az.tetris.event.BlocksRemovedEvent;
import ee.az.tetris.event.DownEvent;
import ee.az.tetris.event.DownTickEvent;
import ee.az.tetris.event.EmitEvent;
import ee.az.tetris.event.Event;
import ee.az.tetris.event.FallEndEvent;
import ee.az.tetris.event.FallStartEvent;
import ee.az.tetris.event.FallTickEvent;
import ee.az.tetris.event.FixBlocksEvent;
import ee.az.tetris.event.LeftEvent;
import ee.az.tetris.event.RightEvent;
import ee.az.tetris.event.TurnEvent;
import ee.az.tetris.timer.EventFiringTimer;
import ee.az.tetris.timer.Multitimer;
import ee.az.tetris.worker.EmitterWorker;
import ee.az.tetris.worker.EmptyWorker;
import ee.az.tetris.worker.FallWorker;
import ee.az.tetris.worker.FixationWorker;
import ee.az.tetris.worker.LeftShiftWorker;
import ee.az.tetris.worker.LineRemovalWorker;
import ee.az.tetris.worker.RightShiftWorker;
import ee.az.tetris.worker.SequenceWorker;
import ee.az.tetris.worker.TurnWorker;
import ee.az.tetris.worker.Worker;
import javax.swing.JFrame;

/**
 * @author Artur Zinatullin (artur.zinatullin@gmail.com) 2010
 */
public class Tetris extends JFrame {

	private static final long serialVersionUID = 1L;

	private final static int WIDTH = 16;
	private final static int HEIGHT = 25;

	public Tetris() {
		Glass glass = new Glass(WIDTH, HEIGHT);
		add(glass);
		Dispatcher dispatcher = new Dispatcher();
		KeyListener keyListener = new KeyListener(dispatcher);
		glass.addKeyListener(keyListener);

		initDispatcher(dispatcher, glass);
		dispatcher.dispatch(new EmitEvent());

		initTimer(dispatcher);
	}

	private void initDispatcher(Dispatcher dispatcher, Glass glass) {
		final FallWorker fallWorker = new FallWorker(glass, dispatcher);

		// user input events
		dispatcher.put(TurnEvent.class, new TurnWorker(glass));
		dispatcher.put(LeftEvent.class, new LeftShiftWorker(glass));
		dispatcher.put(RightEvent.class, new RightShiftWorker(glass));
		dispatcher.put(DownEvent.class, fallWorker);
		dispatcher.put(FallStartEvent.class, fallWorker);

		// timer events
		dispatcher.put(DownTickEvent.class, fallWorker);
		dispatcher.put(FallTickEvent.class, fallWorker);
		
		// logic events
		dispatcher.put(FallEndEvent.class, fallWorker);
		dispatcher.put(FixBlocksEvent.class, new FixationWorker(glass, dispatcher));
		Worker sequenceWorker = new SequenceWorker(glass, new LineRemovalWorker(glass, dispatcher), new EmitterWorker(glass));
		dispatcher.put(BlocksFixedEvent.class, sequenceWorker);
		dispatcher.put(EmitEvent.class, new EmitterWorker(glass));
		
		final EmptyWorker emptyWorker = new EmptyWorker(glass);
		dispatcher.put(BlocksRemovedEvent.class, emptyWorker);
	}

	private void initTimer(Dispatcher dispatcher) {
		Multitimer multitimer = new Multitimer();
		
		multitimer.addTimer(new EventFiringTimer(300, dispatcher) {
			@Override
			protected Event getEvent() {
				return new DownTickEvent();
			}
		});
		multitimer.addTimer(new EventFiringTimer(30, dispatcher) {
			@Override
			protected Event getEvent() {
				return new FallTickEvent();
			}
		});
		
		Thread thread = new Thread(multitimer);
		thread.start();
	}
	
	public static void main(String[] args) {
		try {
			Tetris frame = new Tetris();
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setTitle("Table test");
			frame.pack();
			frame.setResizable(false);
			frame.setVisible(true);
		} catch (Exception e) {
			e.printStackTrace(System.err);
			System.exit(1);
		}
	}
}
